import Observer from "./observer";
import MqttObserver from './mqtt_observer';
import { Client, Message, ConnectionOptions } from 'paho-mqtt';
import Logger from "../utils/logger";

interface IClientManagerOptions extends ConnectionOptions {
    host: string,
    port: number,
}
// MqttManager 类 —— 封装了 MQTT 的连接、断线重连、订阅、发布等完整流程
class MqttManager extends Observer {
  static mqttManager: MqttManager;
  private mqttClient!: Client;
  private topics: Set<string> = new Set();
  private connectOptions: ConnectionOptions = {};
  private reconnectPeriod = 5000;

  public static getInstance() {
    if (this.mqttManager !== undefined) {
      return this.mqttManager;
    }
    this.mqttManager = new MqttManager();
    return this.mqttManager;
  }

  public init(opt: IClientManagerOptions) {
    if (this.mqttClient) {
      try {
        this.mqttClient.disconnect();
      } catch {}
    }
    const clientId = `derucci_hilink_${new Date().getTime()}${Math.floor(Math.random() * 100)}`;
    this.mqttClient = new Client(opt.host, opt.port, clientId);
    this.mqttClient.onConnectionLost = this.onConnectionLost.bind(this);
    this.mqttClient.onMessageArrived = this.onMessageArrived.bind(this);
    this.connectOptions = {
        timeout: 30,
        useSSL: opt.useSSL,
        userName: opt.userName,
        password: opt.password,
        reconnect: true,
        onSuccess: () => {
            this.connected();
        },
        onFailure: (error) => {
            Logger.log("连接失败", error);
            this.reConnect();
        }
    };
    Logger.log("准备连接Mqtt");
    // Logger.log("准备连接Mqtt", this.connectOptions);
    this.connect();
  }

  // 连接Mqtt
  private connect() {
    this.mqttClient.connect(this.connectOptions);
  }

  // reconnectPeriod秒延迟重新连接Mqtt
  private reConnect() {
    Logger.log(`${this.reconnectPeriod/1000}秒后开始重新连接`);
    setTimeout(() => {
        Logger.log(`开始重新连接`);
        this.mqttClient.connect(this.connectOptions);
    }, this.reconnectPeriod)
  }

  private connected() {
    Logger.log("mqtt连接完成");
    this.observers.forEach((observer: MqttObserver) => observer.onConnected());
  }

  private onMessageArrived(message: Message) {
    Logger.log("mqtt 数据：", message.destinationName, (() => {
      try {
        return JSON.parse(message.payloadString);
      } catch {
        return message.payloadString;
      }
    })());
    this.observers.forEach((observer: MqttObserver) => {
      let data: any;
      try {
        data = JSON.parse(message.payloadString);
      } catch {
        data = message.payloadString;
      }
      observer.onMessage(message.destinationName, data);
    });
  }

  private onConnectionLost(responseObject: any) {
    if (responseObject.errorCode !== 0) {
      Logger.log("mqtt断开连接", responseObject.errorMessage);
    } else {
      Logger.log("mqtt断开连接");
    }
    this.observers.forEach((observer: MqttObserver) => observer.onDisconnected());
  }

  public subscribe(topic: string | string[]) {
    const topics = typeof topic === 'string' ? [topic] : topic;
    topics.forEach(t => this.topics.add(t));
    topics.forEach(t => {
      this.mqttClient.subscribe(t, {
        qos: 0,
        onSuccess: () => {
          Logger.log(`mqtt订阅成功: ${t}`);
          this.observers.forEach((observer: MqttObserver) => observer.onSubscribed(t));
        },
        onFailure: (error: any) => {
          Logger.log(`mqtt订阅失败: ${t}`, error);
          this.observers.forEach((observer: MqttObserver) => observer.onSubscribeFail(t, error));
        }
      });
    });
  }

  public unsubscribe(topic: string | string[]) {
    const topics = typeof topic === 'string' ? [topic] : topic;
    topics.forEach(t => {
      this.mqttClient.unsubscribe(t, {
        onSuccess: () => {
          this.topics.delete(t);
          this.observers.forEach((observer: MqttObserver) => observer.onUnsubscribed(t));
        },
        onFailure: (error: any) => {
          this.observers.forEach((observer: MqttObserver) => observer.onUnsubscribeFail(t, error));
        }
      });
    });
  }

  public unsubscribeAll() {
    const topicArr = Array.from(this.topics);
    Logger.log("mqtt取消所有订阅");
    topicArr.forEach(t => {
      this.mqttClient.unsubscribe(t, {
        onSuccess: () => {
          Logger.log("mqtt取消所有订阅成功");
          this.topics.delete(t);
          this.observers.forEach((observer: MqttObserver) => observer.onUnsubscribed(t));
        },
        onFailure: (error: any) => {
          Logger.log("mqtt取消订阅失败", error);
          this.observers.forEach((observer: MqttObserver) => observer.onUnsubscribeFail(t, error));
        }
      });
    });
  }

  public publish(topic: string, message: string, opts?: { qos?: number, retained?: boolean }, callback?: (error?: any) => void) {
    const msg = new Message(message);
    msg.destinationName = topic;
    // if (opts?.qos !== undefined) msg.qos = opts.qos;
    if (opts?.retained !== undefined) msg.retained = opts.retained;

    try {
      this.mqttClient.send(msg);
      Logger.log(`${topic}发布信息成功：${message}`);
      if (callback) callback();
    } catch (error) {
      Logger.log(`${topic}发布信息失败：${message}`, error);
      if (callback) callback(error);
    }
  }
}

export default MqttManager.getInstance();
